local num_flashes = 7
local function reset()
    -- new game
    digiline_send("display", "----**---- Tic-Tac-Toe ----**---- Waiting for players")
    for i = 1, 9 do
        digiline_send(tostring(i), "off#888")
        mem.stat[i] = false
    end

    mem.player[2] = nil
    mem.player[1] = nil
    mem.turn = 1
    mem.winner = nil
    mem.rst_ok = true
    mem.flash_counter = nil
end

local function won(turn)
    for i = 1, 8 do
        local line = mem.lines[i]
        if      mem.stat[line:byte(1) - 48] == turn
            and mem.stat[line:byte(2) - 48] == turn
            and mem.stat[line:byte(3) - 48] == turn
        then
            return line
        end
    end
    return false
end

local function draw()
    return  mem.stat[1] and mem.stat[2] and mem.stat[3]
        and mem.stat[4] and mem.stat[5] and mem.stat[6]
        and mem.stat[7] and mem.stat[8] and mem.stat[9]
end

if event.type == "program" then
    mem.stat = {}
    mem.player = {}
    mem.lines = { "123", "456", "789", "147", "258", "369", "159", "357" }
    reset()
elseif event.type == "digiline" then
    local idx = event.channel:byte(1) - 48

    -- 0x52 = ASCII code of "R"
    if idx < 1 or idx > 9 or #event.channel > 1 or event.msg:byte(1) ~= 0x52
        or mem.winner or mem.stat[idx]
    then
        return -- not for us, or not rightclick, or game end, or cell occupied
    end

    if not mem.player[mem.turn] then
        mem.player[mem.turn] = event.msg:sub(3)
    elseif event.msg:sub(3) ~= mem.player[mem.turn] then
        return -- wrong player
    end

    -- give the player 30 seconds to think the move before allowing reset
    mem.rst_ok = false

    mem.stat[idx] = mem.turn
    digiline_send(tostring(idx), mem.turn == 1 and "on#f44" or "on#0f0")
    local winner_line = won(mem.turn)
    if winner_line then
        digiline_send("display", "Winner: " .. mem.player[mem.turn])
        mem.winner = mem.turn
        interrupt(999, "enable_reset")
        mem.flash_counter = num_flashes
        interrupt(0.5, "flash")
        for i = 1, 9 do
            if      winner_line:byte(1) - 48 ~= i
                and winner_line:byte(2) - 48 ~= i
                and winner_line:byte(3) - 48 ~= i
                and mem.stat[i]
            then
                digiline_send(tostring(i), mem.stat[i] == 1 and "off#f44" or "off#0f0")
            end
        end
    elseif draw() then
        digiline_send("display", "It's a draw!")
        mem.winner = 0
        interrupt(0, "enable_reset")
    else
        mem.turn = 3 - mem.turn
        if not mem.player[mem.turn] then
            digiline_send("display", "Waiting for second player")
        else
            digiline_send("display", mem.player[mem.turn] .. " to play")
        end
        interrupt(30, "enable_reset")
    end
elseif event.type == "on" then
    -- must be reset signal
    if mem.rst_ok then
        reset()
    end
elseif event.type == "interrupt" then
    if event.iid == "enable_reset" then
        -- timer expired - the game can be reset
        mem.rst_ok = true
    elseif event.iid == "flash" then
        mem.flash_counter = mem.flash_counter - 1
        if mem.flash_counter == 0 then
            mem.flash_counter = nil
            interrupt(0, "enable_reset")
            return
        end
        interrupt(0.5, "flash")
        local turn_on, turn_off = "on#f44", "off#f44"
        if mem.winner == 2 then
            turn_on, turn_off = "on#0f0", "off#0f0"
        end
        local turn = mem.flash_counter % 2 == 1 and turn_on or turn_off
        local winner_line = won(mem.turn)
        for i = 1, 3 do
            digiline_send(tostring(winner_line:byte(i) - 48), turn)
        end
    end
end
